
Introduction
To re-cap - we have obtained our raw counts matrices from GEO (or
cellranger). We have performed QC and subset the data according to the
QC plots we generated. We have normalised the data via sctransform -
regressing out unwanted sources of variation, and integrated across our
datasets. Lastly, we have clustered the cells into separate groups based
on the similarity of their expression profiles.
In the next part of the course we are going to explore the genes that
show the most differences in their expression between clusters. The top
genes showing the biggest change in their expression compared to the
other clusters are often called marker genes. We can then use
these marker genes to assign an identity class to our clusters
of interest.

Learning Objectives
- Find differentially expressed genes using
FindMarkers()
and FindAllMarkers().
- Use the bioconductor human annotation package
org.Hs.eg.db to add some more useful annotation to our
marker genes.
- Assign cluster identity using cell type canonical marker genes
- Carry out a simple pathway analysis
Firstly, we will load up the packages we have been using so far for
this part of the course.
library(Seurat, quietly = T)
library(tidyverse, quietly = T)
And then we can load up the R objects we have used in the previous
sessions.
seurat <- readRDS("Robjects/seurat_integrated.RDS")
We can simply run the name of the Seurat object (in our case
seurat) to give us a summary of what the object contains
and what calculations have been performed on it.
seurat
An object of class Seurat
31850 features across 3322 samples within 3 assays
Active assay: integrated (3000 features, 3000 variable features)
2 other assays present: RNA, SCT
3 dimensional reductions calculated: pca, tsne, umap
We can recap our clusters by taking a look at the
DimPlot
DimPlot(seurat, reduction = "umap")

Find differentially expressed genes
Our next objective is to start assigning some biological
meaning to our data. We have some nice clusters but no clue yet
what cell types our clusters contain.
In Seurat we can find the top differentially expressed genes -or
markers - within each cluster. The function FindMarkers()
handles most of the differential expression testing within the Seurat
package and finds marker genes within a particular cluster. As a
default, Seurat performs differential expression based on the
non-parameteric Wilcoxon rank sum test but other methods are available
within the function.
To start with we can find all the differentially expressed genes in
cluster 3 compared to every other cluster. The min.pct
argument requires a gene to be detected at a minimum percentage of all
the cells.
Since we have performed integration in a previous step, we set the
default assay back to ‘RNA’ to ensure we are using the raw counts,
rather than counts transformed by integration, for our differential
analysis.
# We make sure the default assay is set back to "RNA" to use the original counts for DE analysis.
DefaultAssay(seurat) <- "RNA"
cluster3.markers <- FindMarkers(seurat,
ident.1 = 3,
min.pct = 0.25)
cluster3.markers %>% head
A quick look at the top 6 lines in our differentially expressed
marker gene table shows us the format of the data. We have the results
of the Wilcoxon rank sum test as a p value and adjusted p value for
multiple testing. We also have our average log2 fold change in
expression between our clusters. The pct.1 and pct.2 columns show us the
percentage of cells within the cluster the gene is expressed in (we set
a minimum threshold of 20% in the function). As default, the output
table is sorted by adjusted p value.
A littlebit of table manipulation in dplyr makes the
markers easier to view…
cluster3.markers %>%
select(avg_log2FC, p_val, p_val_adj) %>%
slice_max(n = 5, order_by = avg_log2FC)
To check our DE findings, we can visualize expression of a particular
gene of interest across all clusters using the VlnPlot()
function. This shows the expression levels of a gene (or genes) of
interest within the cells across all clusters.
VlnPlot(seurat, features = "LYZ")

We can also check which cells express our gene of interest on the
UMAP image using FeaturePlot
FeaturePlot(seurat, features = "LYZ")

Perhaps we are most interested in the differentially expressed genes
between clusters 1 and 3
cluster1.markers <- FindMarkers(seurat,
ident.1 = 1,
ident.2 = 3,
min.pct = 0.25,verbose = FALSE)
cluster1.markers %>% head()
… or the DE genes between clusters 1 in comparison to 3 and 4?
cluster1.markers <- FindMarkers(seurat, ident.1 = 1,
ident.2 = c(3,4), # we can group clusters together!
min.pct = 0.25,
verbose = FALSE)
cluster1.markers %>% head()
Usually, we are wanting to find the most differentially expressed
genes in each cluster across the whole dataset. For this we use
FindAllMarkers. This time we will only report the
positively expressed markers with a minimum log fold change of 0.25.
markers <- FindAllMarkers(seurat,
only.pos = TRUE,
min.pct = 0.25,
logfc.threshold = 0.25,
verbose = F) # keeps it quiet!
markers
Again, a bit of manipulation in dplyr enables us to get
the top 5 DE genes in each cluster.
markers %>%
select(gene, cluster, avg_log2FC, p_val, p_val_adj) %>%
group_by(cluster) %>% # important to look in each cluster!
slice_max(n = 5, order_by = avg_log2FC)
Exercise
- Use
FindMarkers to find the differentially expressed
genes in clusters 1 & 2 vs 8
- Can you use
dplyr to select columns of interest and
order them by log fold change?
- Use one of the visualisation tools to look at the expression of one
of the top marker genes
- Bonus! - We have not discussed the visualisation tool
RidgePlot. See if you can use it to make another plot of
one of the top marker genes. More info here - https://satijalab.org/seurat/articles/visualization_vignette.html
#Put exercise work in here :-)
Annotating marker genes
We may want to share our findings with collaborators or perform
further bioinformatic analysis on our DE genes in clusters of interest.
In this dataset, the gene level information is given as a gene name.
Whilst this is useful we may want to embellish these results with some
gene description information or some other gene IDs (eg Ensembl,
Entrez).
The org.Hs.eg.db database is a genome wide annotation
package for human that has many different gene mappings based on Entrez
Gene identifiers. These are up to data databases of gene annotation that
can be installed like any other bioconductor package
# Dont run this if you have run already!
#install.packages("BiocManager")
#BiocManager::install("org.Hs.eg.db")
library(org.Hs.eg.db)
In our case we are going to use the gene symbols (rownames in our
Seurat object) to extract the gene description and entrez ID from the
database.
We are going to filter the database by a key or set of keys in order
to extract the information we want. Valid names for the key can be
retrieved with the keytypes function.
keytypes(org.Hs.eg.db)
[1] "ACCNUM" "ALIAS" "ENSEMBL" "ENSEMBLPROT" "ENSEMBLTRANS" "ENTREZID" "ENZYME" "EVIDENCE" "EVIDENCEALL"
[10] "GENENAME" "GENETYPE" "GO" "GOALL" "IPI" "MAP" "OMIM" "ONTOLOGY" "ONTOLOGYALL"
[19] "PATH" "PFAM" "PMID" "PROSITE" "REFSEQ" "SYMBOL" "UCSCKG" "UNIPROT"
We are going to use the keytype ‘SYMBOL’ which designates the gene
names that we have in our rownames of the Seurat object.
Unfortunately, the authors of dplyr and
AnnotationDbi have both decided to use the name
select in their packages. To avoid confusion and problems
from packages attempting to use the wrong tool, the following code is
sometimes used:-
AnnotationDbi::select which tells us to use the ‘select’
function from the AnnotationDBI package.
anno <- AnnotationDbi::select(org.Hs.eg.db,
keys=rownames(seurat),
columns=c("SYMBOL","GENENAME", "ENTREZID"), # info we want to extract
keytype = "SYMBOL") # information we have
# Check it worked!
head(anno)
Now we can combine our annotation table to our marker genes using
dplyr and left_join
anno_markers <- markers %>%
dplyr::select(gene, cluster, avg_log2FC, p_val, p_val_adj) %>%
left_join(anno, by = c("gene" = "SYMBOL"))
anno_markers %>% head()
NA
At this point we might want to save our result into a nicely
formatted file where we can look up our clusters, top DE genes and their
gene descriptions.
anno_markers %>% write_csv("Annotated.results.csv")
Assigning cluster identity
If you recall, our dataset is from the following paper:-
Caron M, St-Onge P, Sontag T, Wang YC et al. Single-cell analysis of
childhood leukemia reveals a link between developmental states and
ribosomal protein expression as a source of intra-individual
heterogeneity. Sci Rep 2020 May15;10(1):8079
This paper uses the following canonical marker genes to
assign cell cluster identity. This starts to give some biological
meaning to our results.
These genes are known to be expressed in a particular cell type.
| CD79A |
B Cells |
| CD34 |
CD34+, |
| MS4A1 |
CD20+ B cells, |
| MZB1 |
BCMA, |
| CST3 |
Monocytes, |
| SPN Im |
mature hematopoietic, |
| HBA1 |
erythrocytes, |
| CD3D |
T cells, |
| NKG7 |
NK cells |
We can assign these marker genes as a variable
cell_markers <- c("CD79A","CD34", "MS4A1", "MZB1","CST3" ,"SPN", "KIT", "HBA1", "CD3D", "NKG7")
Dot plots allow us to examine particular features or genes of
interest across all clusters. Creating a dotplot of the cell type
markers lets us assign identity based on their expression.
DotPlot(seurat, features = cell_markers) + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

We can then manually create new identities for each cluster as
follows.
new.cluster.ids <- c("T Cells", #0
"T Cells", #1
"Erythrocytes", #2
"Monocytes", #3
"B Cells", #4
"CD34+", #5
"B Cells", #6
"NK Cells", #7
"Immature hema#8topoietic",
"CD79A B cells",#9
"Monocytes", #10
"CD20+ B cells", #11
"BCMA", #12
"Erythrocytes", #13
"BCMA") #14
names(new.cluster.ids) <- levels(seurat)
new.cluster.ids
0 1 2 3 4
"T Cells" "T Cells" "Erythrocytes" "Monocytes" "B Cells"
5 6 7 8 9
"CD34+" "B Cells" "NK Cells" "Immature hema#8topoietic" "CD79A B cells"
10 11 12 13 14
"Monocytes" "CD20+ B cells" "BCMA" "Erythrocytes" "BCMA"
seurat_named <- RenameIdents(seurat, new.cluster.ids)
DimPlot(seurat_named, reduction = "umap", label = TRUE, pt.size = 0.5) + NoLegend()

We can check our result to the UMAP plot produced from the full
dataset within the paper - not too bad! Considering we have used a
subset and not followed their exact method. 
GO term and pathway enrichment
Sometimes we want to carry out a Gene Enrichment Analysis on
a list of genes of interest. This enables us to find functional
terms that are statistically over represented within a group of
genes compared to a background list. clusterProfiler is an
R package that provides statistical tests for expression analysis of
terms such as GO (Gene Ontology), within gene lists that have shown
statistically significant differences.
When using this tool we need to have two inputs: a list of genes that
show significant differentiation in expression and a list of background
genes. Hypergeometric tests are used for the statistical enrichment
analysis.
We can check for GO terms measuring BP (biological process), CC (cell
component) and MF (molecular function).
First of all we need to create our test genes and background list
library(clusterProfiler)
background <- anno %>% pull("ENTREZID")
go_markers <- anno_markers %>% filter(cluster ==c(0,1)) %>% pull(ENTREZID) # Lets look at clusters (1 + 2 - T cells)
eGO <- enrichGO(
gene = go_markers,
universe = background,
OrgDb = org.Hs.eg.db,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
readable = TRUE
)
head(eGO)
clusterProfiler also has functionality to plot pathway
diagrams for us. It uses our new object created by enrichGO
as input and highlights the pathways enriched in our dataset.
plotGOgraph(eGO)
$dag
A graphNEL graph with directed edges
Number of Nodes = 65
Number of Edges = 109
$complete.dag
[1] "A graph with 65 nodes."

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgU2luZ2xlLUNlbGwgUk5BLXNlcSAtIFNlc3Npb24gNCIKYXV0aG9yOiAiRW1pbHkgViBDaGFtYmVycyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwotLS0KPGltZyBzcmM9ImltYWdlcy9sb2dvLXNtLnBuZyIgc3R5bGU9InBvc2l0aW9uOmFic29sdXRlO3RvcDo0MHB4O3JpZ2h0OjEwcHg7IiB3aWR0aD0iMjAwIiAvPgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICB0aWR5ID0gVFJVRSwKICB0aWR5Lm9wdHMgPSBsaXN0KHdpZHRoLmN1dG9mZiA9IDk1KSwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFLAogIGZpZy53aWR0aCA9IDEwLAogIHRpbWVfaXQgPSBUUlVFCikKCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRvIHJlLWNhcCAtICB3ZSBoYXZlIG9idGFpbmVkIG91ciByYXcgY291bnRzIG1hdHJpY2VzIGZyb20gR0VPIChvciBjZWxscmFuZ2VyKS4gV2UgaGF2ZSBwZXJmb3JtZWQgUUMgYW5kIHN1YnNldCB0aGUgZGF0YSBhY2NvcmRpbmcgdG8gdGhlIFFDIHBsb3RzIHdlIGdlbmVyYXRlZC4gV2UgaGF2ZSBub3JtYWxpc2VkIHRoZSBkYXRhIHZpYSBzY3RyYW5zZm9ybSAtIHJlZ3Jlc3Npbmcgb3V0IHVud2FudGVkIHNvdXJjZXMgb2YgdmFyaWF0aW9uLCBhbmQgaW50ZWdyYXRlZCBhY3Jvc3Mgb3VyIGRhdGFzZXRzLiBMYXN0bHksIHdlIGhhdmUgY2x1c3RlcmVkIHRoZSBjZWxscyBpbnRvIHNlcGFyYXRlIGdyb3VwcyBiYXNlZCBvbiB0aGUgc2ltaWxhcml0eSBvZiB0aGVpciBleHByZXNzaW9uIHByb2ZpbGVzLgoKSW4gdGhlIG5leHQgcGFydCBvZiB0aGUgY291cnNlIHdlIGFyZSBnb2luZyB0byBleHBsb3JlIHRoZSBnZW5lcyB0aGF0IHNob3cgdGhlIG1vc3QgZGlmZmVyZW5jZXMgaW4gdGhlaXIgZXhwcmVzc2lvbiBiZXR3ZWVuIGNsdXN0ZXJzLiBUaGUgdG9wIGdlbmVzIHNob3dpbmcgdGhlIGJpZ2dlc3QgY2hhbmdlIGluIHRoZWlyIGV4cHJlc3Npb24gY29tcGFyZWQgdG8gdGhlIG90aGVyIGNsdXN0ZXJzIGFyZSBvZnRlbiBjYWxsZWQgKm1hcmtlciBnZW5lcyouIFdlIGNhbiB0aGVuIHVzZSB0aGVzZSBtYXJrZXIgZ2VuZXMgdG8gYXNzaWduIGFuICppZGVudGl0eSBjbGFzcyogdG8gb3VyIGNsdXN0ZXJzIG9mIGludGVyZXN0LgoKIVtdKGltYWdlcy9mbG93Y2hhcnQzLnBuZykKCiMjIExlYXJuaW5nIE9iamVjdGl2ZXMKKiBGaW5kIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB1c2luZyBgRmluZE1hcmtlcnMoKWAgYW5kIGBGaW5kQWxsTWFya2VycygpYC4KKiBVc2UgdGhlIGJpb2NvbmR1Y3RvciBodW1hbiBhbm5vdGF0aW9uIHBhY2thZ2UgYG9yZy5Icy5lZy5kYmAgdG8gYWRkIHNvbWUgbW9yZSB1c2VmdWwgYW5ub3RhdGlvbiB0byBvdXIgbWFya2VyIGdlbmVzLgoqIEFzc2lnbiBjbHVzdGVyIGlkZW50aXR5IHVzaW5nIGNlbGwgdHlwZSBjYW5vbmljYWwgbWFya2VyIGdlbmVzCiogQ2Fycnkgb3V0IGEgc2ltcGxlIHBhdGh3YXkgYW5hbHlzaXMKCkZpcnN0bHksIHdlIHdpbGwgbG9hZCB1cCB0aGUgcGFja2FnZXMgd2UgaGF2ZSBiZWVuIHVzaW5nIHNvIGZhciBmb3IgdGhpcyBwYXJ0IG9mIHRoZSBjb3Vyc2UuCgpgYGB7cn0KbGlicmFyeShTZXVyYXQsIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KHRpZHl2ZXJzZSwgcXVpZXRseSA9IFQpCmBgYApBbmQgdGhlbiB3ZSBjYW4gbG9hZCB1cCB0aGUgUiBvYmplY3RzIHdlIGhhdmUgdXNlZCBpbiB0aGUgcHJldmlvdXMgc2Vzc2lvbnMuCmBgYHtyfQpzZXVyYXQgPC0gcmVhZFJEUygiUm9iamVjdHMvc2V1cmF0X2ludGVncmF0ZWQuUkRTIikKYGBgCldlIGNhbiBzaW1wbHkgcnVuIHRoZSBuYW1lIG9mIHRoZSBTZXVyYXQgb2JqZWN0IChpbiBvdXIgY2FzZSBgc2V1cmF0YCkgdG8gZ2l2ZSB1cyBhIHN1bW1hcnkgb2Ygd2hhdCB0aGUgb2JqZWN0IGNvbnRhaW5zIGFuZCB3aGF0IGNhbGN1bGF0aW9ucyBoYXZlIGJlZW4gcGVyZm9ybWVkIG9uIGl0LgpgYGB7cn0Kc2V1cmF0CmBgYApXZSBjYW4gcmVjYXAgb3VyIGNsdXN0ZXJzIGJ5IHRha2luZyBhIGxvb2sgYXQgdGhlIGBEaW1QbG90YApgYGB7cn0KRGltUGxvdChzZXVyYXQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKYGBgCgoKIyBGaW5kIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwpPdXIgbmV4dCBvYmplY3RpdmUgaXMgdG8gc3RhcnQgYXNzaWduaW5nIHNvbWUgKmJpb2xvZ2ljYWwgbWVhbmluZyogdG8gb3VyIGRhdGEuIFdlIGhhdmUgc29tZSBuaWNlIGNsdXN0ZXJzIGJ1dCBubyBjbHVlIHlldCB3aGF0IGNlbGwgdHlwZXMgb3VyIGNsdXN0ZXJzIGNvbnRhaW4uCgpJbiBTZXVyYXQgd2UgY2FuIGZpbmQgdGhlIHRvcCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgLW9yIG1hcmtlcnMgLSB3aXRoaW4gZWFjaCBjbHVzdGVyLiBUaGUgZnVuY3Rpb24gYEZpbmRNYXJrZXJzKClgIGhhbmRsZXMgbW9zdCBvZiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdGVzdGluZyB3aXRoaW4gdGhlIFNldXJhdCBwYWNrYWdlIGFuZCBmaW5kcyBtYXJrZXIgZ2VuZXMgd2l0aGluIGEgcGFydGljdWxhciBjbHVzdGVyLiBBcyBhIGRlZmF1bHQsIFNldXJhdCBwZXJmb3JtcyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiYXNlZCBvbiB0aGUgbm9uLXBhcmFtZXRlcmljIFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYnV0IG90aGVyIG1ldGhvZHMgYXJlIGF2YWlsYWJsZSB3aXRoaW4gdGhlIGZ1bmN0aW9uLiAKClRvIHN0YXJ0IHdpdGggd2UgY2FuIGZpbmQgYWxsIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gY2x1c3RlciAzIGNvbXBhcmVkIHRvIGV2ZXJ5IG90aGVyIGNsdXN0ZXIuIFRoZSBgbWluLnBjdGAgYXJndW1lbnQgcmVxdWlyZXMgYSBnZW5lIHRvIGJlIGRldGVjdGVkIGF0IGEgbWluaW11bSBwZXJjZW50YWdlIG9mIGFsbCB0aGUgY2VsbHMuCgpTaW5jZSB3ZSBoYXZlIHBlcmZvcm1lZCBpbnRlZ3JhdGlvbiBpbiBhIHByZXZpb3VzIHN0ZXAsIHdlIHNldCB0aGUgZGVmYXVsdCBhc3NheSBiYWNrIHRvICdSTkEnIHRvIGVuc3VyZSB3ZSBhcmUgdXNpbmcgdGhlIHJhdyBjb3VudHMsIHJhdGhlciB0aGFuIGNvdW50cyB0cmFuc2Zvcm1lZCBieSBpbnRlZ3JhdGlvbiwgZm9yIG91ciBkaWZmZXJlbnRpYWwgYW5hbHlzaXMuCmBgYHtyfQojIFdlIG1ha2Ugc3VyZSB0aGUgZGVmYXVsdCBhc3NheSBpcyBzZXQgYmFjayB0byAiUk5BIiB0byB1c2UgdGhlIG9yaWdpbmFsIGNvdW50cyBmb3IgREUgYW5hbHlzaXMuCkRlZmF1bHRBc3NheShzZXVyYXQpIDwtICJSTkEiCmBgYAoKCmBgYHtyfQpjbHVzdGVyMy5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHNldXJhdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgID0gMC4yNSkKY2x1c3RlcjMubWFya2VycyAlPiUgaGVhZApgYGAKQSBxdWljayBsb29rIGF0IHRoZSB0b3AgNiBsaW5lcyBpbiBvdXIgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIG1hcmtlciBnZW5lIHRhYmxlIHNob3dzIHVzIHRoZSBmb3JtYXQgb2YgdGhlIGRhdGEuIFdlIGhhdmUgdGhlIHJlc3VsdHMgb2YgdGhlIFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYXMgYSBwIHZhbHVlIGFuZCBhZGp1c3RlZCBwIHZhbHVlIGZvciBtdWx0aXBsZSB0ZXN0aW5nLiBXZSBhbHNvIGhhdmUgb3VyIGF2ZXJhZ2UgbG9nMiBmb2xkIGNoYW5nZSBpbiBleHByZXNzaW9uIGJldHdlZW4gb3VyIGNsdXN0ZXJzLiBUaGUgcGN0LjEgYW5kIHBjdC4yIGNvbHVtbnMgc2hvdyB1cyB0aGUgcGVyY2VudGFnZSBvZiBjZWxscyB3aXRoaW4gdGhlIGNsdXN0ZXIgdGhlIGdlbmUgaXMgZXhwcmVzc2VkIGluICh3ZSBzZXQgYSBtaW5pbXVtIHRocmVzaG9sZCBvZiAyMCUgaW4gdGhlIGZ1bmN0aW9uKS4gQXMgZGVmYXVsdCwgdGhlIG91dHB1dCB0YWJsZSBpcyBzb3J0ZWQgYnkgYWRqdXN0ZWQgcCB2YWx1ZS4KCkEgbGl0dGxlYml0IG9mIHRhYmxlIG1hbmlwdWxhdGlvbiBpbiBgZHBseXJgIG1ha2VzIHRoZSBtYXJrZXJzIGVhc2llciB0byB2aWV3Li4uCmBgYHtyfQpjbHVzdGVyMy5tYXJrZXJzICAlPiUgCiAgc2VsZWN0KGF2Z19sb2cyRkMsIHBfdmFsLCBwX3ZhbF9hZGopICU+JQogIHNsaWNlX21heChuID0gNSwgb3JkZXJfYnkgPSBhdmdfbG9nMkZDKSAKYGBgClRvIGNoZWNrIG91ciBERSBmaW5kaW5ncywgd2UgY2FuIHZpc3VhbGl6ZSBleHByZXNzaW9uIG9mIGEgcGFydGljdWxhciBnZW5lIG9mIGludGVyZXN0IGFjcm9zcyBhbGwgY2x1c3RlcnMgdXNpbmcgdGhlIGBWbG5QbG90KClgIGZ1bmN0aW9uLiBUaGlzIHNob3dzIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiBhIGdlbmUgKG9yIGdlbmVzKSBvZiBpbnRlcmVzdCB3aXRoaW4gdGhlIGNlbGxzIGFjcm9zcyBhbGwgY2x1c3RlcnMuCgpgYGB7cn0KVmxuUGxvdChzZXVyYXQsIGZlYXR1cmVzID0gIkxZWiIpCmBgYApXZSBjYW4gYWxzbyBjaGVjayB3aGljaCBjZWxscyBleHByZXNzIG91ciBnZW5lIG9mIGludGVyZXN0IG9uIHRoZSBVTUFQIGltYWdlIHVzaW5nIGBGZWF0dXJlUGxvdGAKCmBgYHtyfQpGZWF0dXJlUGxvdChzZXVyYXQsIGZlYXR1cmVzID0gIkxZWiIpCmBgYApQZXJoYXBzIHdlIGFyZSBtb3N0IGludGVyZXN0ZWQgaW4gdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBiZXR3ZWVuIGNsdXN0ZXJzIDEgYW5kIDMKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmNsdXN0ZXIxLm1hcmtlcnMgPC0gRmluZE1hcmtlcnMoc2V1cmF0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4xID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMiA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgPSAwLjI1LHZlcmJvc2UgPSBGQUxTRSkKY2x1c3RlcjEubWFya2VycyAlPiUgaGVhZCgpCmBgYAouLi4gb3IgdGhlIERFIGdlbmVzIGJldHdlZW4gY2x1c3RlcnMgMSBpbiBjb21wYXJpc29uIHRvIDMgYW5kIDQ/CmBgYHtyfQpjbHVzdGVyMS5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHNldXJhdCwgaWRlbnQuMSA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPSBjKDMsNCksICMgd2UgY2FuIGdyb3VwIGNsdXN0ZXJzIHRvZ2V0aGVyIQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKY2x1c3RlcjEubWFya2VycyAlPiUgaGVhZCgpCmBgYApVc3VhbGx5LCB3ZSBhcmUgd2FudGluZyB0byBmaW5kIHRoZSBtb3N0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiBlYWNoIGNsdXN0ZXIgYWNyb3NzIHRoZSB3aG9sZSBkYXRhc2V0LiBGb3IgdGhpcyB3ZSB1c2UgYEZpbmRBbGxNYXJrZXJzYC4gVGhpcyB0aW1lIHdlIHdpbGwgb25seSByZXBvcnQgdGhlIHBvc2l0aXZlbHkgZXhwcmVzc2VkIG1hcmtlcnMgd2l0aCBhIG1pbmltdW0gbG9nIGZvbGQgY2hhbmdlIG9mIDAuMjUuCgpgYGB7cn0KbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhzZXVyYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKSAjIGtlZXBzIGl0IHF1aWV0IQptYXJrZXJzCmBgYAoKQWdhaW4sIGEgYml0IG9mIG1hbmlwdWxhdGlvbiBpbiBgZHBseXJgIGVuYWJsZXMgdXMgdG8gZ2V0IHRoZSB0b3AgNSBERSBnZW5lcyBpbiBlYWNoIGNsdXN0ZXIuCmBgYHtyfQptYXJrZXJzICU+JQogIHNlbGVjdChnZW5lLCBjbHVzdGVyLCBhdmdfbG9nMkZDLCBwX3ZhbCwgcF92YWxfYWRqKSAlPiUKICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAjIGltcG9ydGFudCB0byBsb29rIGluIGVhY2ggY2x1c3RlciEKICAgIHNsaWNlX21heChuID0gNSwgb3JkZXJfYnkgPSBhdmdfbG9nMkZDKQpgYGAKIyMgRXhlcmNpc2UKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBVc2UgYEZpbmRNYXJrZXJzYCB0byBmaW5kIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gY2x1c3RlcnMgMSAmIDIgdnMgOAotIENhbiB5b3UgdXNlIGBkcGx5cmAgdG8gc2VsZWN0IGNvbHVtbnMgb2YgaW50ZXJlc3QgYW5kIG9yZGVyIHRoZW0gYnkgbG9nIGZvbGQgY2hhbmdlPwotIFVzZSBvbmUgb2YgdGhlIHZpc3VhbGlzYXRpb24gdG9vbHMgdG8gbG9vayBhdCB0aGUgZXhwcmVzc2lvbiBvZiBvbmUgb2YgdGhlIHRvcCBtYXJrZXIgZ2VuZXMKLSBCb251cyEgLSBXZSBoYXZlIG5vdCBkaXNjdXNzZWQgdGhlIHZpc3VhbGlzYXRpb24gdG9vbCBgUmlkZ2VQbG90YC4gU2VlIGlmIHlvdSBjYW4gdXNlIGl0IHRvIG1ha2UgYW5vdGhlciBwbG90IG9mIG9uZSBvZiB0aGUgdG9wIG1hcmtlciBnZW5lcy4gTW9yZSBpbmZvIGhlcmUgLSBodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3Zpc3VhbGl6YXRpb25fdmlnbmV0dGUuaHRtbAo8L2Rpdj4KCmBgYHtyfQojUHV0IGV4ZXJjaXNlIHdvcmsgaW4gaGVyZSA6LSkKCgpgYGAKCgojIEFubm90YXRpbmcgbWFya2VyIGdlbmVzCgpXZSBtYXkgd2FudCB0byBzaGFyZSBvdXIgZmluZGluZ3Mgd2l0aCBjb2xsYWJvcmF0b3JzIG9yIHBlcmZvcm0gZnVydGhlciBiaW9pbmZvcm1hdGljIGFuYWx5c2lzIG9uIG91ciBERSBnZW5lcyBpbiBjbHVzdGVycyBvZiBpbnRlcmVzdC4gSW4gdGhpcyBkYXRhc2V0LCB0aGUgZ2VuZSBsZXZlbCBpbmZvcm1hdGlvbiBpcyBnaXZlbiBhcyBhIGdlbmUgbmFtZS4gV2hpbHN0IHRoaXMgaXMgdXNlZnVsIHdlIG1heSB3YW50IHRvIGVtYmVsbGlzaCB0aGVzZSByZXN1bHRzIHdpdGggc29tZSBnZW5lIGRlc2NyaXB0aW9uIGluZm9ybWF0aW9uIG9yIHNvbWUgb3RoZXIgZ2VuZSBJRHMgKGVnIEVuc2VtYmwsIEVudHJleikuCgpUaGUgYG9yZy5Icy5lZy5kYmAgZGF0YWJhc2UgaXMgYSBnZW5vbWUgd2lkZSBhbm5vdGF0aW9uIHBhY2thZ2UgZm9yIGh1bWFuIHRoYXQgaGFzIG1hbnkgZGlmZmVyZW50IGdlbmUgbWFwcGluZ3MgYmFzZWQgb24gRW50cmV6IEdlbmUgaWRlbnRpZmllcnMuIFRoZXNlIGFyZSB1cCB0byBkYXRhIGRhdGFiYXNlcyBvZiBnZW5lIGFubm90YXRpb24gdGhhdCBjYW4gYmUgaW5zdGFsbGVkIGxpa2UgYW55IG90aGVyIGJpb2NvbmR1Y3RvciBwYWNrYWdlCgpgYGB7cn0KIyBEb250IHJ1biB0aGlzIGlmIHlvdSBoYXZlIHJ1biBhbHJlYWR5IQojaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQojQmlvY01hbmFnZXI6Omluc3RhbGwoIm9yZy5Icy5lZy5kYiIpCgpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKYGBgCgpJbiBvdXIgY2FzZSB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBnZW5lIHN5bWJvbHMgKHJvd25hbWVzIGluIG91ciBTZXVyYXQgb2JqZWN0KSB0byBleHRyYWN0IHRoZSBnZW5lIGRlc2NyaXB0aW9uIGFuZCBlbnRyZXogSUQgZnJvbSB0aGUgZGF0YWJhc2UuCgpXZSBhcmUgZ29pbmcgdG8gZmlsdGVyIHRoZSBkYXRhYmFzZSBieSBhIGtleSBvciBzZXQgb2Yga2V5cyBpbiBvcmRlciB0byBleHRyYWN0IHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50LiBWYWxpZCBuYW1lcyBmb3IgdGhlIGtleSBjYW4gYmUgcmV0cmlldmVkIHdpdGggdGhlIGBrZXl0eXBlc2AgZnVuY3Rpb24uCgpgYGB7cn0Ka2V5dHlwZXMob3JnLkhzLmVnLmRiKQpgYGAKV2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUga2V5dHlwZSAnU1lNQk9MJyB3aGljaCBkZXNpZ25hdGVzIHRoZSBnZW5lIG5hbWVzIHRoYXQgd2UgaGF2ZSBpbiBvdXIgcm93bmFtZXMgb2YgdGhlIFNldXJhdCBvYmplY3QuCgpVbmZvcnR1bmF0ZWx5LCB0aGUgYXV0aG9ycyBvZiBgZHBseXJgIGFuZCBgQW5ub3RhdGlvbkRiaWAgaGF2ZSBib3RoIGRlY2lkZWQgdG8gdXNlIHRoZSBuYW1lIGBzZWxlY3RgIGluIHRoZWlyIHBhY2thZ2VzLiBUbyBhdm9pZCBjb25mdXNpb24gYW5kIHByb2JsZW1zIGZyb20gcGFja2FnZXMgYXR0ZW1wdGluZyB0byB1c2UgdGhlIHdyb25nIHRvb2wsIHRoZSBmb2xsb3dpbmcgY29kZSBpcyBzb21ldGltZXMgdXNlZDotCgpgQW5ub3RhdGlvbkRiaTo6c2VsZWN0YCB3aGljaCB0ZWxscyB1cyB0byB1c2UgdGhlICdzZWxlY3QnIGZ1bmN0aW9uIGZyb20gdGhlIGBBbm5vdGF0aW9uREJJYCBwYWNrYWdlLgpgYGB7cn0KYW5ubyA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlzPXJvd25hbWVzKHNldXJhdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbnM9YygiU1lNQk9MIiwiR0VORU5BTUUiLCAiRU5UUkVaSUQiKSwgICMgaW5mbyB3ZSB3YW50IHRvIGV4dHJhY3QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJTWU1CT0wiKSAjIGluZm9ybWF0aW9uIHdlIGhhdmUKCiMgQ2hlY2sgaXQgd29ya2VkIQpoZWFkKGFubm8pICAKYGBgCk5vdyB3ZSBjYW4gY29tYmluZSBvdXIgYW5ub3RhdGlvbiB0YWJsZSB0byBvdXIgbWFya2VyIGdlbmVzIHVzaW5nIGBkcGx5cmAgYW5kIGBsZWZ0X2pvaW5gCmBgYHtyfQphbm5vX21hcmtlcnMgPC0gbWFya2VycyAlPiUgCiAgZHBseXI6OnNlbGVjdChnZW5lLCBjbHVzdGVyLCBhdmdfbG9nMkZDLCBwX3ZhbCwgcF92YWxfYWRqKSAlPiUKICBsZWZ0X2pvaW4oYW5ubywgYnkgPSBjKCJnZW5lIiA9ICJTWU1CT0wiKSkKCmFubm9fbWFya2VycyAlPiUgaGVhZCgpCgpgYGAKQXQgdGhpcyBwb2ludCB3ZSBtaWdodCB3YW50IHRvIHNhdmUgb3VyIHJlc3VsdCBpbnRvIGEgbmljZWx5IGZvcm1hdHRlZCBmaWxlIHdoZXJlIHdlIGNhbiBsb29rIHVwIG91ciBjbHVzdGVycywgdG9wIERFIGdlbmVzIGFuZCB0aGVpciBnZW5lIGRlc2NyaXB0aW9ucy4KCmBgYHtyfQphbm5vX21hcmtlcnMgJT4lIHdyaXRlX2NzdigiQW5ub3RhdGVkLnJlc3VsdHMuY3N2IikKYGBgCgojIEFzc2lnbmluZyBjbHVzdGVyIGlkZW50aXR5CgpJZiB5b3UgcmVjYWxsLCBvdXIgZGF0YXNldCBpcyBmcm9tIHRoZSBmb2xsb3dpbmcgcGFwZXI6LQoKPiBDYXJvbiBNLCBTdC1PbmdlIFAsIFNvbnRhZyBULCBXYW5nIFlDIGV0IGFsLiBTaW5nbGUtY2VsbCBhbmFseXNpcyBvZiBjaGlsZGhvb2QgbGV1a2VtaWEgcmV2ZWFscyBhIGxpbmsgYmV0d2VlbiBkZXZlbG9wbWVudGFsIHN0YXRlcyBhbmQgcmlib3NvbWFsIHByb3RlaW4gZXhwcmVzc2lvbiBhcyBhIHNvdXJjZSBvZiBpbnRyYS1pbmRpdmlkdWFsIGhldGVyb2dlbmVpdHkuIFNjaSBSZXAgMjAyMCBNYXkxNTsxMCgxKTo4MDc5CgpUaGlzIHBhcGVyIHVzZXMgdGhlIGZvbGxvd2luZyAqY2Fub25pY2FsIG1hcmtlciBnZW5lcyogdG8gYXNzaWduIGNlbGwgY2x1c3RlciBpZGVudGl0eS4gVGhpcyBzdGFydHMgdG8gZ2l2ZSBzb21lICpiaW9sb2dpY2FsIG1lYW5pbmcqIHRvIG91ciByZXN1bHRzLgoKVGhlc2UgZ2VuZXMgYXJlIGtub3duIHRvIGJlIGV4cHJlc3NlZCBpbiBhIHBhcnRpY3VsYXIgY2VsbCB0eXBlLgoKR2VuZSAgICBDZWxsIHR5cGUKLS0tLSAgICAtLS0tCkNENzlBICAgQiBDZWxscwpDRDM0ICAgIENEMzQrLCAKTVM0QTEgICBDRDIwKyBCIGNlbGxzLCAKTVpCMSAgICBCQ01BLApDU1QzICAgIE1vbm9jeXRlcywgClNQTiAgIEltbWF0dXJlIGhlbWF0b3BvaWV0aWMsIApIQkExICAgIGVyeXRocm9jeXRlcywgCkNEM0QgICAgVCBjZWxscywgCk5LRzcgICAgTksgY2VsbHMKCgpXZSBjYW4gYXNzaWduIHRoZXNlIG1hcmtlciBnZW5lcyBhcyBhIHZhcmlhYmxlCmBgYHtyfQpjZWxsX21hcmtlcnMgPC0gYygiQ0Q3OUEiLCJDRDM0IiwgIk1TNEExIiwgIk1aQjEiLCJDU1QzIiAsIlNQTiIsICJLSVQiLCAiSEJBMSIsICJDRDNEIiwgIk5LRzciKQpgYGAKCkRvdCBwbG90cyBhbGxvdyB1cyB0byBleGFtaW5lIHBhcnRpY3VsYXIgZmVhdHVyZXMgb3IgZ2VuZXMgb2YgaW50ZXJlc3QgYWNyb3NzIGFsbCBjbHVzdGVycy4gQ3JlYXRpbmcgYSBkb3RwbG90IG9mIHRoZSBjZWxsIHR5cGUgbWFya2VycyBsZXRzIHVzIGFzc2lnbiBpZGVudGl0eSBiYXNlZCBvbiB0aGVpciBleHByZXNzaW9uLgpgYGB7cn0KRG90UGxvdChzZXVyYXQsIGZlYXR1cmVzID0gY2VsbF9tYXJrZXJzKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCldlIGNhbiB0aGVuIG1hbnVhbGx5IGNyZWF0ZSBuZXcgaWRlbnRpdGllcyBmb3IgZWFjaCBjbHVzdGVyIGFzIGZvbGxvd3MuCgpgYGB7cn0KbmV3LmNsdXN0ZXIuaWRzIDwtIGMoIlQgQ2VsbHMiLCAjMAogICAgICAgICAgICAgICAgICAgICAiVCBDZWxscyIsICMxCiAgICAgICAgICAgICAgICAgICAgICJFcnl0aHJvY3l0ZXMiLCAjMiAgCiAgICAgICAgICAgICAgICAgICAgICJNb25vY3l0ZXMiLCAgIzMKICAgICAgICAgICAgICAgICAgICAgIkIgQ2VsbHMiLCAjNAogICAgICAgICAgICAgICAgICAgICAiQ0QzNCsiLCAjNQogICAgICAgICAgICAgICAgICAgICAiQiBDZWxscyIsICM2CiAgICAgICAgICAgICAgICAgICAgICJOSyBDZWxscyIsICM3CiAgICAgICAgICAgICAgICAgICAgICJJbW1hdHVyZSBoZW1hIzh0b3BvaWV0aWMiLCAKICAgICAgICAgICAgICAgICAgICAgIkNENzlBIEIgY2VsbHMiLCM5IAogICAgICAgICAgICAgICAgICAgICAiTW9ub2N5dGVzIiwgIzEwCiAgICAgICAgICAgICAgICAgICAgICJDRDIwKyBCIGNlbGxzIiwgIzExCiAgICAgICAgICAgICAgICAgICAgICJCQ01BIiwgIzEyCiAgICAgICAgICAgICAgICAgICAgICJFcnl0aHJvY3l0ZXMiLCAjMTMgCiAgICAgICAgICAgICAgICAgICAgICJCQ01BIikgIzE0Cm5hbWVzKG5ldy5jbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldXJhdCkKbmV3LmNsdXN0ZXIuaWRzCnNldXJhdF9uYW1lZCA8LSBSZW5hbWVJZGVudHMoc2V1cmF0LCBuZXcuY2x1c3Rlci5pZHMpCmBgYAoKYGBge3J9CkRpbVBsb3Qoc2V1cmF0X25hbWVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgcHQuc2l6ZSA9IDAuNSkgKyBOb0xlZ2VuZCgpCmBgYApXZSBjYW4gY2hlY2sgb3VyIHJlc3VsdCB0byB0aGUgVU1BUCBwbG90IHByb2R1Y2VkIGZyb20gdGhlIGZ1bGwgZGF0YXNldCB3aXRoaW4gdGhlIHBhcGVyIC0gbm90IHRvbyBiYWQhIENvbnNpZGVyaW5nIHdlIGhhdmUgdXNlZCBhIHN1YnNldCBhbmQgbm90IGZvbGxvd2VkIHRoZWlyIGV4YWN0IG1ldGhvZC4KIVtdKGltYWdlcy91bWFwcy5wbmcpCgoKCiMgR08gdGVybSBhbmQgcGF0aHdheSBlbnJpY2htZW50ClNvbWV0aW1lcyB3ZSB3YW50IHRvIGNhcnJ5IG91dCBhICpHZW5lIEVucmljaG1lbnQgQW5hbHlzaXMqIG9uIGEgbGlzdCBvZiBnZW5lcyBvZiBpbnRlcmVzdC4gVGhpcyBlbmFibGVzIHVzIHRvIGZpbmQgKmZ1bmN0aW9uYWwgdGVybXMqIHRoYXQgYXJlIHN0YXRpc3RpY2FsbHkgb3ZlciByZXByZXNlbnRlZCB3aXRoaW4gYSBncm91cCBvZiBnZW5lcyBjb21wYXJlZCB0byBhIGJhY2tncm91bmQgbGlzdC4gYGNsdXN0ZXJQcm9maWxlcmAgaXMgYW4gUiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgc3RhdGlzdGljYWwgdGVzdHMgZm9yIGV4cHJlc3Npb24gYW5hbHlzaXMgb2YgdGVybXMgc3VjaCBhcyBHTyAoR2VuZSBPbnRvbG9neSksIHdpdGhpbiBnZW5lIGxpc3RzIHRoYXQgaGF2ZSBzaG93biBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2VzLiAKCldoZW4gdXNpbmcgdGhpcyB0b29sIHdlIG5lZWQgdG8gaGF2ZSB0d28gaW5wdXRzOiBhIGxpc3Qgb2YgZ2VuZXMgdGhhdCBzaG93IHNpZ25pZmljYW50IGRpZmZlcmVudGlhdGlvbiBpbiBleHByZXNzaW9uIGFuZCBhIGxpc3Qgb2YgYmFja2dyb3VuZCBnZW5lcy4gSHlwZXJnZW9tZXRyaWMgdGVzdHMgYXJlIHVzZWQgZm9yIHRoZSBzdGF0aXN0aWNhbCBlbnJpY2htZW50IGFuYWx5c2lzLiAKCldlIGNhbiBjaGVjayBmb3IgR08gdGVybXMgbWVhc3VyaW5nIEJQIChiaW9sb2dpY2FsIHByb2Nlc3MpLCBDQyAoY2VsbCBjb21wb25lbnQpIGFuZCBNRiAobW9sZWN1bGFyIGZ1bmN0aW9uKS4KCkZpcnN0IG9mIGFsbCB3ZSBuZWVkIHRvIGNyZWF0ZSBvdXIgdGVzdCBnZW5lcyBhbmQgYmFja2dyb3VuZCBsaXN0CgpgYGB7cn0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmJhY2tncm91bmQgPC0gYW5ubyAlPiUgcHVsbCgiRU5UUkVaSUQiKQoKZ29fbWFya2VycyA8LSBhbm5vX21hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09YygwLDEpKSAlPiUgcHVsbChFTlRSRVpJRCkgIyBMZXRzIGxvb2sgYXQgY2x1c3RlcnMgKDEgKyAyIC0gIFQgY2VsbHMpCgogIGVHTyA8LSBlbnJpY2hHTygKICAgIGdlbmUgICAgICAgICAgPSBnb19tYXJrZXJzLAogICAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kLCAKICAgIE9yZ0RiICAgICAgICAgPSBvcmcuSHMuZWcuZGIsCiAgICBvbnQgICAgICAgICAgID0gIkJQIiwKICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgcHZhbHVlQ3V0b2ZmICA9IDAuMDUsCiAgICByZWFkYWJsZSAgICAgID0gVFJVRQogICkKICAKaGVhZChlR08pCmBgYApgY2x1c3RlclByb2ZpbGVyYCBhbHNvIGhhcyBmdW5jdGlvbmFsaXR5IHRvIHBsb3QgcGF0aHdheSBkaWFncmFtcyBmb3IgdXMuIEl0IHVzZXMgb3VyIG5ldyBvYmplY3QgY3JlYXRlZCBieSBgZW5yaWNoR09gIGFzIGlucHV0IGFuZCBoaWdobGlnaHRzIHRoZSBwYXRod2F5cyBlbnJpY2hlZCBpbiBvdXIgZGF0YXNldC4KCmBgYHtyfQpwbG90R09ncmFwaChlR08pCmBgYAoKCgo=